
"""
总共6个tile，i = 0~5
每个tile 4 个等级 j = 0~3

我可以拿到的是 
    throughput 网络总吞吐量
    buffer 情况
    FoV 视野情况
"""

import numpy as np

# wi,j，每个tile中的码率等级



tile_adjacent_relation = {
                        0:[2,3,4,5],
                        1:[2,3,4,5],
                        2:[0,1,4,5],
                        3:[0,1,4,5],
                        4:[0,1,2,3],
                        5:[0,1,2,3]}

# state_rate_mapping = {0: 8,
#                       1: 24,
#                       2: 36,
#                       3: 48}  # 单位MBps
# state_rate_mapping = {0: 0.2,
#                            1: 2,
#                            2: 12,
#                            3: 24}
# state_rate_mapping = {0: 0.2,
#                            1: 0.8,
#                            2: 2.4,
#                            3: 12}
# state_rate_mapping = {0: 0.8,
#                            1: 2.4,
#                            2: 4.8,
#                            3: 12}
state_rate_mapping = {0: 0.15,
                           1: 2.4,
                           2: 6.4,
                           3: 16}

max_buffer = 10.5 # 取实际情况的接近的最大值

def calculate_buffer(action,throughput,buffer):
    if max(action) >3:
        print(action)
        print("3")
    throughput_total = 0
    for i in range(6):
        throughput_total += throughput[i]

    rebuffer_event = 0
    rebuffer_flag = 0
    rebuffer_record_time = 0
    for i in range(6):
        #print(i,buffer[i],throughput[i],action[i])
        if buffer[i] < 1:
            buffer[i] += throughput[i] / state_rate_mapping[action[i]]
            if buffer[i] < 1:
                rebuffer_event += 1 - buffer[i]
                rebuffer_flag = 1
                rebuffer_record_time += 1
                # self.buffer[i] = 1
        else:
            buffer[i] = 1 + throughput[i] / state_rate_mapping[action[i]]
    if not rebuffer_flag:
        buffer = (np.array(buffer) - 1) #.tolist()  # 模拟buffer过程

    buffer = np.array(buffer)

    buffer[buffer>max_buffer] = max_buffer

    buffer = buffer.tolist()

    return buffer,rebuffer_event,rebuffer_record_time

# 用FoV信息获取下一时期的价值
"""
考虑
1s后的视野内，外，邻近
2s后的视野内，外，邻近 * 0.5
"""


def value(FoV,buffer): # FoV传下一时刻和下下时刻 buffer传现在时刻的
    predict_state_value = [[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]
# in and non   
    for i in range(6):
        if FoV[0][i] == 1: # 第1s

            # b = (buffer[i]+ max_buffer/3) / max_buffer  # b参数
            for j in range(4):
                b = (0.025 * buffer[i] * j if buffer[i] > max_buffer / 2 else 0.1 * buffer[i] / (
                            j + 1))  # case 1 0.5-3 case 2
                predict_state_value[i][j] += b*state_rate_mapping[j] # in FoV
                # near FoV
                for k in range(4):
                    # b = (buffer[k] + max_buffer / 3) / max_buffer
                    b = (0.025 * buffer[k] * j if buffer[k] > max_buffer / 2 else 0.1 * buffer[k] / (j + 1))
                    predict_state_value[tile_adjacent_relation[i][k]][j] += b*state_rate_mapping[j]*0.75 # 每一个tile的每个的码率等级都要计算和记录

        if FoV[1][i] == 1: # 第2s
            # b = (buffer[i] + max_buffer / 3) / max_buffer
            for j in range(4):
                b = (0.025 * buffer[i] * j if buffer[i] > max_buffer / 2 else 0.1 * buffer[i] / (
                        j + 1))  # case 1 0.5-3 case 2
                predict_state_value[i][j] += b*state_rate_mapping[j]*0.5
                # near FoV
                for k in range(4):
                    # b = (buffer[k] + max_buffer / 3) / max_buffer
                    b = (0.025 * buffer[k] * j if buffer[k] > max_buffer / 2 else 0.1 * buffer[k] / (j + 1))
                    predict_state_value[tile_adjacent_relation[i][k]][j] += b*state_rate_mapping[j]*0.25
    # predict_state_value = (np.array(predict_state_value)*1.5).tolist()
    return predict_state_value

def choose_bitrate_level(value_func,throughput,FoV):
    choose_result = [0,0,0,0,0,0]

    C = 0
    base_line = state_rate_mapping[0] * 6 # 首先保证每一个tile都至少选上0
    sum_weight = 0 + base_line # 提前把那个基准值留出来
    artificial_range = []
    for i in range(6):
        C += throughput[i]  # C是实际容量
    # C = throughput_mean * 6

    for i in range(6):  # 这里暂时不考虑near FoV的tile，因为两个1的时候不好操作
        if FoV[i] == 1:
            artificial_range.append(i)
    for i in range(6):
        if FoV[i] == 0:
            artificial_range.append(i)
    
    # print(artificial_range)

    if C > base_line: # 如果一开始就不够，就全0就好
        for i in artificial_range: # 每个tile分别决策 顺序的话改这个数组就挺好
            max_level = 0
            max_record = value_func[i][0]/state_rate_mapping[0]
            for j in range(3):
                temp = value_func[i][j+1]#(value_func[i][j+1] - value_func[i][j])/(state_rate_mapping[j+1] - state_rate_mapping[j])
                # temp = (value_func[i][j+1] - value_func[i][j])/(state_rate_mapping[j+1] - state_rate_mapping[j])
                if temp >= max_record:
                    max_record = temp
                    max_level = j+1
                    # if max_level == 4:
                    #     print(max_level,max_record)
            choose_result[i] = max_level
            
            sum_weight += state_rate_mapping[max_level] - state_rate_mapping[0]
            if sum_weight > C:
                if max_level == 1:
                    choose_result[i] = 0
                    break
                if max_level == 2:
                    if sum_weight - state_rate_mapping[2] + state_rate_mapping[1] <= C:
                        choose_result[i] = 1
                        break
                    else:
                        choose_result[i] = 0
                        break
                if max_level == 3:
                    if sum_weight - state_rate_mapping[3] + state_rate_mapping[2] <= C:
                        choose_result[i] = 2
                        break
                    else:
                        if sum_weight - state_rate_mapping[3] + state_rate_mapping[1] <= C:
                            choose_result[i] = 1
                            break
                        else:
                            choose_result[i] = 0
                            break
    return choose_result




def QoE(FoV, action, rebuffer_event):
    # 码率  ## 用字典吧，直接加
    FoV_Adjacent = {0: [2, 3, 4, 5],
                    1: [2, 3, 4, 5],
                    2: [0, 1, 4, 5],
                    3: [0, 1, 4, 5],
                    4: [0, 1, 2, 3],
                    5: [0, 1, 2, 3]}
    # 现在的逻辑是第一个FoV为1的当成主视角，也就是最高优先级 主视角0.8 主视角相邻的tile中 有1的给0.2，0的给0.1
    artificial_range_1s = {}
    range_temp = [0, 1, 2, 3, 4, 5]
    main_perspective = 0
    main_near = []
    artificial_range_2s = {}
    for i in range_temp:
        if FoV[i].astype(int) == 1:
            # count = list(FoV[self.video_slot + 1].astype(int)).count(1)
            artificial_range_1s[i] = 1.2  # 主视野内权重为0.8
            range_temp.remove(i)
            main_perspective = i
            for j in FoV_Adjacent[i]:
                range_temp.remove(j)
                if FoV[j].astype(int) == 1:
                    artificial_range_1s[j] = 0.8
                    main_near.append(j)
                else:
                    artificial_range_1s[j] = 0.2

        artificial_range_1s[range_temp[0]] = 0

    bitrate_level_total = 0
    # 乘哪个FoV这个判断直接在state中考量，，等一下，有点问题
    for i in range(6):  # 6个面
        bitrate_level_total += action[i] * artificial_range_1s[i]

    # 帧内平滑度
    # 考虑FoV的东西，这个平滑度考虑主视角和其他四片的差异
    smoothness_within_frame = 0
    for i in FoV_Adjacent[main_perspective]:
        if i in main_near:
            smoothness_within_frame += abs(action[main_perspective] - action[i])
        else:
            smoothness_within_frame += abs(0.5 * (action[main_perspective] - action[i]))

    alpha = 1.2
    beta = 0.5
    gamma = 2.5
    reward = alpha * bitrate_level_total - beta * smoothness_within_frame - gamma * rebuffer_event

    return reward

